import requests
import json
import re
import base64
import argparse
import time
import os
import sys
from urllib.parse import urlparse, parse_qs, unquote, urljoin

# 默认配置信息（可以被命令行参数覆盖）
TARGET_URL = "http://qq.com/"
BASE_URL = "http://10.90.87.77/lfradius/libs/portal/unify/"
USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"

# 公网IP检测服务
PUBLIC_IP_SERVICES = [
    "https://ddns.oray.com/checkip",
    "http://ipv4.icanhazip.com",
    "http://ident.me",
    "http://ifconfig.me/ip"
]

# 调试功能
DEBUG = False
DEBUG_DETAIL = False


def debug_print(message, level=1):
    if DEBUG:
        indent = "  " * level
        print(f"{indent}{message}")


def debug_response(response, title="Response", level=1):
    if DEBUG:
        indent = "  " * level
        print(f"{indent}{title}:")
        print(f"{indent}  Status: {response.status_code}")
        print(f"{indent}  URL: {response.url}")

        if DEBUG_DETAIL and response.text:
            print(f"{indent}  Content (first 1000 chars):")
            print(f"{indent}  {response.text[:1000]}")
        elif response.text:
            print(f"{indent}  Content length: {len(response.text)}")
        else:
            print(f"{indent}  No content")


def extract_query_params(url):
    """从URL中提取查询参数"""
    parsed = urlparse(url)
    return parse_qs(parsed.query)


def get_initial_redirect():
    """获取初始重定向信息"""
    debug_print("步骤1: 获取初始重定向", level=0)
    try:
        # 添加超时设置
        response = session.get(TARGET_URL, allow_redirects=False, timeout=10)
        debug_response(response, "初始响应", level=1)

        if response.status_code == 302:
            redirect_url = response.headers.get("Location", "")
            debug_print(f"重定向URL: {redirect_url}", level=1)
            return redirect_url

        debug_print(f"错误: 未获得预期的重定向 (状态码: {response.status_code})", level=1)
        return None
    except Exception as e:
        debug_print(f"获取重定向时出错: {str(e)}", level=1)
        return None


def get_auth_page(redirect_url):
    """获取认证页面并提取必要参数"""
    debug_print("步骤2: 获取认证页面", level=0)
    try:
        response = session.get(redirect_url, timeout=10)
        debug_response(response, "认证页面响应", level=1)

        if response.status_code == 200:
            # 改进BASIP提取逻辑
            basip_match = re.search(r'name="basip"\s+value="([^"]+)"', response.text)
            basip = basip_match.group(1) if basip_match else "192.168.195.229"
            debug_print(f"提取的BASIP: {basip}", level=1)

            # 改进URL参数解析
            parsed = urlparse(redirect_url)
            params = parse_qs(parsed.query, keep_blank_values=True)

            # 解码URL编码的值
            wlanuserip = unquote(params.get("wlanuserip", [""])[0])
            clientmac = unquote(params.get("clientmac", [""])[0])
            nasid = unquote(params.get("nasid", ["1"])[0])

            debug_print(f"提取的参数:", level=1)
            debug_print(f"  wlanuserip: {wlanuserip}", level=1)
            debug_print(f"  clientmac: {clientmac}", level=1)
            debug_print(f"  nasid: {nasid}", level=1)

            return {
                "basip": basip,
                "wlanuserip": wlanuserip,
                "clientmac": clientmac,
                "nasid": nasid
            }

        debug_print(f"错误: 无法加载认证页面 (状态码: {response.status_code})", level=1)
        return None
    except Exception as e:
        debug_print(f"获取认证页面时出错: {str(e)}", level=1)
        return None


def perform_ldap_auth(params, username, password):
    """执行LDAP认证"""
    debug_print("步骤3: 执行LDAP认证", level=0)
    # 修复URL构造错误
    ldap_url = "http://10.90.87.77/lfradius/libs/appmanagement/ldap/start.php"
    debug_print(f"LDAP认证URL: {ldap_url}", level=1)

    payload = {
        "usrname": username,
        "passwd": password,
        "treaty": "on",
        "nasid": params["nasid"]
    }

    try:
        # 添加Referer头
        headers = {"Referer": f"{BASE_URL}portal.php/login/main/nasid/{params['nasid']}/"}
        response = session.post(ldap_url, data=payload, headers=headers, timeout=10)
        debug_response(response, "LDAP认证响应", level=1)

        # 检查响应状态码和内容（根据HAR文档，响应状态码为200）
        return response.status_code == 200
    except Exception as e:
        debug_print(f"执行LDAP认证时出错: {str(e)}", level=1)
        return False


def perform_cmcc_login(params, username, password):
    """执行CMCC登录"""
    debug_print("步骤4: 执行CMCC登录", level=0)
    login_url = f"{BASE_URL}portal.php/login/cmcc_login/"

    # 第一次登录请求数据
    login_data = {
        "usrname": username,
        "passwd": password,
        "treaty": "on",
        "nasid": params["nasid"],
        "usrmac": params["clientmac"],
        "usrip": params["wlanuserip"],
        "basip": params["basip"],
        "success": f"{BASE_URL}portal.php/login/success/",
        "fail": f"{BASE_URL}portal.php/login/fail/",
        "offline": "1",
        "portal_version": "1",
        "portal_papchap": "pap",
        "portal_get": ""
    }

    try:
        # 第一次登录请求
        response = session.post(login_url, data=login_data, timeout=10)
        debug_response(response, "第一次登录响应", level=1)

        if response.status_code != 200:
            return False

        # 第二次登录请求数据 (Base64编码)
        encrypted_value = {
            "usrname": username,
            "passwd": password,
            "usrmac": params["clientmac"],
            "usrip": params["wlanuserip"],
            "basip": params["basip"],
            "nasid": params["nasid"],
            "success": f"{BASE_URL}portal.php/login/success/",
            "fail": f"{BASE_URL}portal.php/login/fail/",
            "portal_get": "",
            "v": 1,
            "t": "pap"
        }

        json_str = json.dumps(encrypted_value)
        cmcc_value = base64.b64encode(json_str.encode()).decode()

        # 第二次登录请求
        payload = {"cmcc_login_value": cmcc_value}
        response = session.post(login_url, data=payload, timeout=10)
        debug_response(response, "第二次登录响应", level=1)

        return response.status_code == 200
    except Exception as e:
        debug_print(f"执行CMCC登录时出错: {str(e)}", level=1)
        return False


def get_public_ip():
    """尝试获取公网IP地址"""
    debug_print("步骤5: 获取公网IP地址", level=0)

    # 尝试多个服务，增加可靠性
    for service in PUBLIC_IP_SERVICES:
        try:
            debug_print(f"尝试从 {service} 获取IP地址", level=1)
            response = session.get(service, timeout=10)

            if response.status_code == 200:
                ip_address = response.text.strip()

                # 验证IP地址格式
                if re.match(r'^\d{1,3}(\.\d{1,3}){3}$', ip_address):
                    debug_print(f"成功获取公网IP: {ip_address}", level=1)
                    return ip_address
                else:
                    debug_print(f"无效的IP地址格式: {ip_address}", level=1)

            debug_print(f"从 {service} 获取IP失败 (状态码: {response.status_code})", level=1)
        except Exception as e:
            debug_print(f"从 {service} 获取IP时出错: {str(e)}", level=1)

    return None


def main(username, password, debug=False, detail=False):
    global DEBUG, DEBUG_DETAIL
    DEBUG = debug
    DEBUG_DETAIL = detail

    # 设置全局会话
    global session
    session = requests.Session()
    session.headers.update({
        "User-Agent": USER_AGENT,
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
        "Accept-Language": "zh,zh-CN;q=0.9,zh-HK;q=0.8",
        "Cache-Control": "no-cache",
        "Pragma": "no-cache",
        "Upgrade-Insecure-Requests": "1"
    })

    debug_print("=" * 50, level=0)
    debug_print("校园网登录脚本启动", level=0)
    debug_print(f"用户名: {username}", level=0)
    debug_print(f"目标网站: {TARGET_URL}", level=0)
    debug_print("=" * 50, level=0)

    # 获取初始重定向
    redirect_url = get_initial_redirect()
    if not redirect_url:
        debug_print("错误: 无法获取重定向URL，退出", level=0)
        return False

    # 获取认证参数
    auth_params = get_auth_page(redirect_url)
    if not auth_params:
        debug_print("错误: 无法获取认证参数，退出", level=0)
        return False

    # 执行登录流程
    if not perform_ldap_auth(auth_params, username, password):
        debug_print("错误: LDAP认证失败，退出", level=0)
        return False

    if not perform_cmcc_login(auth_params, username, password):
        debug_print("错误: CMCC登录失败，退出", level=0)
        return False

    # 等待网络连接建立
    debug_print("等待网络连接建立...", level=0)
    time.sleep(3)  # 等待3秒让网络连接建立

    # 验证公网连接
    debug_print("尝试获取公网IP地址", level=0)
    public_ip = get_public_ip()

    if public_ip:
        debug_print(f"登录成功! 网络已连接，公网IP: {public_ip}", level=0)
        return True

    # 如果首次尝试失败，再试一次
    debug_print("首次获取IP失败，再试一次...", level=0)
    time.sleep(2)  # 等待2秒
    public_ip = get_public_ip()

    if public_ip:
        debug_print(f"登录成功! 网络已连接，公网IP: {public_ip}", level=0)
        return True

    debug_print("错误: 无法获取公网IP，登录可能失败", level=0)
    return False


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='校园网登录脚本')
    parser.add_argument('-u', '--username', required=True, help='校园网用户名')
    parser.add_argument('-p', '--password', required=True, help='校园网密码')
    parser.add_argument('--debug', action='store_true', help='启用调试模式')
    parser.add_argument('--detail', action='store_true', help='显示详细响应内容')
    args = parser.parse_args()

    # 运行主程序
    result = main(args.username, args.password, debug=args.debug, detail=args.detail)

    if not args.debug:
        print("登录成功!" if result else "登录失败!")

    # 设置适当的退出码
    sys.exit(0 if result else 1)